﻿using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Collections.Generic;

namespace OrangePrj
{
    [Serializable]
    public class Work : ICloneable, IComparable
    {
        #region Поля и Свойства

        public int id;
        public string Name { get; set;}
        public string Description { get; set; }
        public double Time { get; set; }
        protected List<int> depind;
        protected WorkList depworks;
        protected WorkList reverseDepWorks = new WorkList();
        public Event beginEvent;
        public Event endEvent;
        public int rank;
        public double timestart;
        public double timeend;
        public int ResID = 0;

        public double Probability { get; set; }
        public double Hard { get; set; }
        protected WorkType type;
        protected int refworkInd = -1;
        protected Work refWork;
        public List<int> EventGroup;
        public List<int> TypeGroup;
        public bool isMissed = false;
        public bool isAdded = false;

        internal Resource Resource { get; set; }

        /// <summary>
        /// Индекс работы в которую возвращается работа возврата. Для обычных работ равен -1
        /// </summary>
        public int RefworkInd
        {
            get
            {
                return refworkInd;
            }
            set
            {
                refworkInd = value;
            }
        }
        public double Ch_Prob { get; set; }
        public WorkType Type
        {
            get
            {
                return type;
            }
            set
            {
                type = value;
            }
        }

        public List<int> DepInd
        {
            get
            {
                return depind;
            }
            set
            {
                depind = value;
            }
        }
        public WorkList DepWorks
        {
            get
            {
                return depworks;
            }
            set
            {
                depworks = value;
            }
        }

        /// <summary>
        /// Возвращает строку в которых перечислены названия всех работ, от которых зависит данная работа.
        /// </summary>
        public string DepWorksNames
        {
            get
            {
                StringBuilder s = new StringBuilder();
                foreach (Work w in depworks)
                {
                    s.Append(w.Name);
                    s.Append(", ");
                }

                return s.ToString();
            }
        }

        public WorkList ReversDepWorks
        {
            get
            {
                return reverseDepWorks;
            }
            set
            {
                reverseDepWorks = value;
            }
        }
        public Work RefWork
        {
            get
            {
                return refWork;
            }
            set
            {
                //refWork = new Work(); что это было? Заглушка?
                refWork = value;
            }
        }
        
        #endregion

        #region Конструкторы
        public Work() 
            : this(null, null, 0, 0, new Resource())
        {
        }
        public Work(string name, string descr)
            : this(name, descr, 0, 0, new Resource())
        {
        }
        public Work(string name, string descr, double time, WorkType type, Resource res) 
            :  this(name, descr, time, type, res, 1, 1, 1)
        {
        }
        public Work(Work w)
        {
            this.id=w.id;
            this.Name=w.Name;
            this.Description=w.Description;
            this.Resource=w.Resource;
            this.Time=w.Time;
            this.beginEvent=w.beginEvent;
            this.endEvent=w.endEvent;
            this.rank=w.rank;
            this.timestart=w.timestart;
            this.timeend=w.timeend;
            this.ResID=w.ResID;
            this.Probability=w.Probability;
            this.Hard=w.Hard;
            this.Ch_Prob=w.Ch_Prob;
            this.type=w.type;
            this.refworkInd = w.refworkInd;
            this.refWork=w.refWork;
            this.EventGroup=w.EventGroup;
            this.TypeGroup=w.TypeGroup;
            this.isMissed=w.isMissed;
            this.isAdded=w.isAdded;
            this.rank = w.rank;
            this.DepWorks = new WorkList();
            foreach (Work work in w.DepWorks)
            {
                this.DepWorks.Add((Work)work.Clone());
            }
            this.depind = new List<int>();
            foreach (int id in w.DepInd)
            {
                this.DepInd.Add(id);
            }
        }

        public Work(string name, string descr, double time, WorkType type, Resource res, double probability, double hard, double ch_prob)
        {
            this.depind = new List<int>();
            this.depworks = new WorkList();
            this.reverseDepWorks = new WorkList();
            this.EventGroup = new List<int>();
            this.TypeGroup = new List<int>();
            this.Name = name;
            this.Description = descr;
            this.Time = time;
            this.type = type;
            this.Resource = res;
            this.Probability = probability;
            this.Hard = hard;
            this.Ch_Prob = ch_prob;

            rank = 0;
        }
        #endregion  

        #region ICloneable
        public Object Clone()
        {
            return new Work(this);
        }
        #endregion

        #region IComparable
        /// <summary>
        /// Сравнивает работы по рангу
        /// </summary>
        /// <param name="obj">работа для сравнения</param>
        /// <returns></returns>
        public int CompareTo(object obj)
        {
            Work w = obj as Work;
            if (this.rank == w.rank)
            {
                return 0;
            } else
            {
                return this.rank < w.rank ? -1 : 1;
            }
        }
        #endregion

        public override string ToString()
        {
            return Name;
        }
        public override bool Equals(object o)
        {
            if (o is Work)
            {
                Work w = (Work)o;
                if (this.Name == w.Name)
                    return true;
                else
                    return false;
            }
            else return false;
        }
        public bool hasDepends()
        {
            if (depind.Count != 0)
                return true;
            else
                return false;
        }
        public void setMaxRank()
        {
            int r = 0;
            foreach (Work w in depworks)
            {
                if (w.rank > r)
                    r = w.rank;
            }
            this.rank = r + 1;
        }
        public void CalcEndTime()
        {
            timeend = (timestart + Time);
        }
        public double getMaxEndtime4Depend()
        {
            double t = 0;
            foreach (Work w in depworks)
                if (w.timeend > t)
                    t = w.timeend;
            return t;
        }
        public double getFullReserve()
        {
            double t = double.MaxValue;
            double df;
            foreach (Work w in reverseDepWorks)
            {
                df = w.timestart - this.timeend;
                if (df < t)
                    t = df;
            }
            if (t == double.MaxValue)
                return -1;
            else
                return t;
        }
        public bool CompareDepInd(Work w)
        {
            bool check = true;

            if (this.DepInd.Count != w.DepInd.Count)
                check = false;
            else
            {
                foreach (int i in this.DepInd)
                {
                    check &= w.DepInd.Contains(i);
                }
            }
            return check;
        }

        /// <summary>
        /// Проверяет есть ли в списке зависимостей, работа с таким именем
        /// </summary>
        /// <param name="Name">Имя для проверки</param>
        /// <returns>Возвращает ссылку на работу или null если работы с таким именем нет в списке зависимостей</returns>
        private Work ContainsDepByName(string Name)
        {
            foreach (Work w in DepWorks)
            {
                if (w.Name == Name)
                {
                    return w;
                }
            }
            return null;
        }
        /// <summary>
        /// Добавляет зависимость
        /// </summary>
        /// <param name="w">Работа от которой зависит данная</param>
        /// <returns>"true" если работа была добавлени, false в противном случае</returns>
        public bool SetDependence(Work w)
        {
            if (DepWorks.Contains(w) || w == this || (ContainsDepByName(w.Name) != null))
            {
                return false;
            }
            else
            {
                DepWorks.Add(w);
                DepInd.Add(w.id);
                return true;
            }
        }
        /// <summary>
        /// Добавляет несколько зависимостей
        /// </summary>
        /// <param name="lstWorks">Работы от которых будет зависеть данная работа</param>
        /// <returns>true - если все работы были добавлены в список зависимостей, false - если хотя бы одна работа не была добавлена</returns>
        public bool SetDependences(WorkList lstWorks)
        {
            bool flag = true;
            foreach (Work w in lstWorks)
            {
                if (!SetDependence(w))
                {
                    flag = false;
                }
            }

            return flag;
        }
        /// <summary>
        /// Удаляет работы из списка зависимостей
        /// </summary>
        /// <param name="w">Работа, которая должна быть удалена из списка зависимостей</param>
        /// <returns>true если удаление прошло успешно, false в противном случае</returns>
        public bool RemoveDependence(Work w)
        {
            Work temp;
            if (!DepWorks.Contains(w) || w == this)
            {
                if ((temp = ContainsDepByName(w.Name)) != null)
                {
                    return RemoveDependence(temp);
                }

                return false;
            }
            else
            {
                DepWorks.Remove(w);
                DepInd.Remove(w.id);
                return true;
            }
        }
        /// <summary>
        /// Удаляет несколько зависимостей
        /// </summary>
        /// <param name="lstWorks">Список работ которые нужно удалить из списка зависимостей</param>
        /// <returns>true если все работы были успешно удалены из списка зависимостей, false в противном случае</returns>
        public bool RemoveDependences(WorkList lstWorks)
        {
            bool flag = true;
            foreach (Work w in lstWorks)
            {
                if (!RemoveDependence(w))
                {
                    flag = false;
                }
            }

            return flag;
        }

        public bool isAlternative()
        {
            if (this.Type == WorkType.ИЛИ || this.Type == WorkType.Искл_ИЛИ)
                return true;
            else
                return false;
        }

        public bool isOneGroup(List<int> dep)
        {
            if (this.DepInd.Count != dep.Count)
                return false;
            else
            {
                foreach (int i in this.DepInd)
                {
                    if (dep.Contains(i) == false)
                        return false;
                }
                return true;
            }
        }

        public bool isSpare()
        {
            if (this.DepWorks.Count != 0)
            {
                foreach (Work w in this.DepWorks)
                {
                    if (!w.isMissed)
                    {
                        return false;
                    }
                }
                return true;
            }
            else
                return false;
        }
    }
}